/*    Copyright 2010 Tobias Marschall
 *
 *    This file is part of MoSDi.
 *
 *    MoSDi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    MoSDi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoSDi.  If not, see <http://www.gnu.org/licenses/>.
 */

package mosdi.matching;

public abstract class AbstractMatcher extends Matcher {
	
	protected int[] pattern;
	
	protected AbstractMatcher(int alphabetSize, int[] pattern) {
		super(alphabetSize);
		this.pattern = pattern;
	}
	
	@Override
	public AlgorithmCostScheme costScheme() {
		return new CostScheme();
	}

	protected static class WindowData {
		protected boolean match;
		protected int shift;
		protected int cost;
		protected WindowData(boolean match, int shift, int cost) {
			this.match = match;
			this.shift = shift;
			this.cost = cost;
		}
	}

	protected abstract WindowData processWindow(int[] text, int windowEndPos);
	
	/** Returns the number of positions in the text that are covered by pattern occurrences. */
	public int coveredPositions(int[] text) {
		int result = 0;
		int lastMatchPos = -1;
		int pos = pattern.length - 1; 
		while (pos<text.length) {
			WindowData wd = processWindow(text, pos);
			if (wd.match) {
				result += Math.min(pattern.length, pos-lastMatchPos);
				lastMatchPos = pos;
			}
			pos += wd.shift;
		}
		return result;
	}
	
	@Override
	public int findMatches(int[] text) {
		characterAccesses = 0;
		int matches = 0;
		// last position of search window
		int pos = pattern.length - 1; 
		while (pos<text.length) {
			WindowData wd = processWindow(text, pos);
			if (wd.match) matches += 1;
			pos += wd.shift;
			characterAccesses += wd.cost;
		}
		return matches;
	}

	private class CostScheme extends AlgorithmCostScheme {
		@Override
		public int alphabetSize() {
			return alphabetSize;
		}

		@Override
		public int maxWindowCost() {
			return pattern.length;
		}

		@Override
		public int shift(int[] windowContent) {
			if (windowContent.length!=pattern.length) throw new IllegalArgumentException("Window size mismatch.");
			WindowData wd = processWindow(windowContent, windowContent.length-1);
			return wd.shift;
		}

		@Override
		public int windowCost(int[] windowContent) {
			if (windowContent.length!=pattern.length) throw new IllegalArgumentException("Window size mismatch.");
			WindowData wd = processWindow(windowContent, windowContent.length-1);
			return wd.cost;
		}

		@Override
		public int windowSize() {
			return pattern.length;
		}
	}

}
